/*
 ============================================================================
 * encoder_lib.h
 *
 *  Created on: 2011-06-11
 *      Author: Shah Hossain
 ============================================================================
 */

#ifndef ENCODER_LIB_H_
#define ENCODER_LIB_H_

extern int DEBUG;

char* shiftParity(char*);
int getParity(char*);
void insertParity(char*,int);

void insertBIT(char*,int,int,int);
void changeBIT(char*,int,int);

void printStream(char*,int);
int getSize(char*);

char* hamEncode (char *orig_data)
{
	char *data= (char *) malloc(getSize(orig_data)+1);

	if (data==NULL)
		printf("\n\n!!!!!!!!!!!!!DNULL\n\n");

	strcpy(data,orig_data);
	int sz=getSize(data);

	char *encoded=NULL;
	int parity;

	encoded =shiftParity(data);
	parity =getParity(encoded);
	insertParity(encoded,parity);

	if (encoded==NULL)
			printf("\n\n!!!!!!!!!!!!!ENULL\n\n");

	//printf("\n\n\n");
	//printStream(data,1);
	//printStream(encoded,1);
	//printf("%s\n",encoded);

	if (DEBUG==1) printf("Original Message:%s\tSize: %d\tParity:%#x\n",data,sz,parity);
	if (DEBUG==1) printStream(data,1);
	if (DEBUG==1) printf("Hamming encoded message: --\tSize: %d\n",getSize(encoded));

	return encoded;
}

int getParity(char *data)
{
	int parity=0;
	int sz=getSize(data);
	int i,j;

	for(i=0; i<sz; i++)
	{
		char chr=data[i];
		char masked_bit;
		for (j=0; j<8; j++)
		{
			masked_bit= chr & 0x01;
			if (masked_bit==1)
			{
				parity ^= (8*(sz-1-i)+j)+1;
				//if (DEBUG==1) printf("val:%d  (%d-%d)\t",(8*(sz-1-i)+j)+1,(sz-1-i),j);
			}

			//if (DEBUG==1) printf("%d-%d:%#x\t",i,j,parity);
			chr=chr>>1;
		}
	}
	return parity;
}

char* shiftParity(char *src)
{
	char* dest=NULL;
	unsigned char left_byte=*src;
	int sz=getSize(src);
	int bits_used=sz*8;
	int k;
	for (k=0; k<8; k++)
		{
			if (left_byte== 0x00)
			{
				bits_used=(sz-1)*8+k;
				break;
			}
			else
				left_byte = left_byte >> 1;
		}


	int space= log2(bits_used);
	//if (DEBUG==1) printf("BITS used: %d\tSpace: %d\n",bits_used,space);
	//space=sz-((bits_used+space)/8)+1;
	space = (int) (ceil(((float) (bits_used+space))/8)-sz);

	//space=(int) log2(bits_used)/8+1;


	if (DEBUG==1) printf("BITS used: %d\tSpace: %d\t Sz: %d\n",bits_used,space,sz);

	//int space= (int) log2(sz*8)/8+1;

	if((src[0]>>(8-((int) log2(sz*8))))==0x00)
		space=0;

	int i;

	dest= (char *) malloc(sz+space);
	strcpy(dest,src);

	//Make space for parity bits + shift bytes
	for (i=sz; i>=0; i--)
		dest[i+space]=src[i];

	//new MSB initialisation
	int j;
	for (j=0; j<space; j++)
		dest[j] = 0x00;  //should be 00, but used for '/0'

	int bitnum=1;
	while(bitnum<((sz+space)*8))
	{
		insertBIT(dest,(bitnum-1),0,(sz+space));
		bitnum *=2;
	}
	return dest;
}

void insertParity(char *data,int parity)
{
	int next_parity=1;

	while(parity != 0)
	{
		char masked_bit = parity & 0x01;
		if (masked_bit==1)
			changeBIT(data,(next_parity-1),1);

		next_parity *=2;
		parity = parity >> 1;
	}
}

void changeBIT(char *data,int bitnum, int bitval)
{
	int bytenum=(bitnum/8);
	int sz=getSize(data);
	if (bitval==1)  //set
		data[sz-1-bytenum] |= 1 << bitnum%8;
	else if (bitval==-1)//flip
		data[sz-1-bytenum] ^= 1 << bitnum%8;
	else	//reset
	{
		unsigned char mask=0xFF;
		mask ^= 1 << bitnum%8;  //flip bit
		data[sz-1-bytenum] &= mask; //reset bit
	}
}

void insertBIT(char *data,int bitnum,int bitvalue,int sz)
{
	char *next_chr=data+1;

	int bytenum=(bitnum/8)+1;
	int i;

	for(i=sz; i>bytenum; i--)
	{
		//printf("size %d     data: %x     next: %s\n", i, *data, next_chr);

		char LSB=*next_chr>>7;
		LSB &= 0x01;
		*data= ((*data<<1) +(LSB)); //sign copy?
		data++;
		next_chr++;
	}

	unsigned char bit_left=*data;
	unsigned char bit_right=*data;

	bit_left= bit_left >> (bitnum%8);
	bit_left= bit_left << (bitnum%8 +1);

	bit_right=bit_right << (8-bitnum%8);
	bit_right=bit_right >> (8-bitnum%8);

	*data = bit_left+ bit_right;
	*data |= bitvalue <<bitnum%8;
}

void printStream(char *data, int mode)
{
	char * cpy=data;

	switch (mode)
	{
		case 0:  //hex
			while (*cpy!='\0')
			{
				printf("%x ",(int) *cpy);
				cpy++;
			}
			break;
		case 1:	//binary
			while (*cpy!='\0')
			{
				int i=0;
				for (i=0; i<8; i++)
				{
					char tmp = *cpy;
					if (i==4) printf(" ");

					if ((tmp &= 0x80>>i)==0)
						printf("0");
					else
						printf("1");
				}

				printf(" ");
				cpy++;
			}
			break;
	}


	printf("\n");
}

//return size in bytes, index starts at 1
int getSize(char *data)
{
	int sz=0;
	while (*data!='\0')
		{
			data++;
			sz++;
		}
	return sz;
}

#endif /* ENCODER_LIB_H_ */
